#define xuser mixc::powerful_cat
#include"mixc.hpp"
#include"concurrency/thread.hpp"
#include"concurrency/thread_self.hpp"

#include"io/event_poll.hpp"
#include"net/tcp.hpp"
#include"net/http.hpp"
#include"utils/shared.hpp"
#include"mixc.hpp"

#include"chrono/now.hpp"
#include"chrono/time.hpp"
#include"utils/tick.hpp"
#include"utils/unique_array.hpp"
#include"lang/cxx.hpp"
#include"net/dns.hpp"






#include<openssl/ssl.h>
#include"io/private/path_buffer.hpp"

#define inc     mixc::powerful_cat

// #pragma push_macro("xhint")
// #undef xhint
// #define xhint(...)

#include"configure/env.hpp"

enum class ssl_role{
    server,
    client,
};

struct ssl_context_info{
    inc::c08    cert_path;
    inc::c08    private_key_path;
    ssl_role    role;
};

voidp openssl_create_ctx(ssl_context_info const & info){
    using buf_t = inc::path_buffer;
    auto && buf                     = buf_t{};
    auto cert_path                  = buf.alloc(info.cert_path);
    auto private_key_path           = buf.alloc(info.private_key_path);

    xdefer{
        buf.free(cert_path, info.cert_path);
        buf.free(private_key_path, info.private_key_path);
    };

    SSL_library_init();
    SSL_load_error_strings();
    OpenSSL_add_all_algorithms();

    auto method = info.role == ssl_role::server ? TLS_server_method() : TLS_client_method();
    auto ctx = SSL_CTX_new(method);

    // TODO:
    if (ctx == nullptr){
        ;
    }

    // 启用自动选择圆锥曲线密钥交换算法，更安全一些
    SSL_CTX_set_ecdh_auto(ctx, true);
    SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
    SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);

    auto code = ixx{};
        (code = SSL_CTX_use_certificate_file(ctx, asciis(cert_path), SSL_FILETYPE_PEM)) > 0 and
        (code = SSL_CTX_use_PrivateKey_file(ctx, asciis(private_key_path), SSL_FILETYPE_PEM)) > 0 and
        (code = SSL_CTX_check_private_key(ctx)) > 0;
    
    // TODO:
    if (code < 0){
        SSL_CTX_free(ctx);
        return nullptr;
    }

    return ctx;
}

#include<fcntl.h>

voidp openssl_create_ssl(voidp context, ::handler peer){
    auto old_flag                   = ::fcntl(peer, F_GETFL, 0);
    auto new_flag                   = old_flag | O_NONBLOCK;
    ::fcntl(peer, F_SETFL, new_flag);

    auto ctx                        = (SSL_CTX *)context;
    auto ssl                        = SSL_new(ctx);
    SSL_set_fd(ssl, peer);
    return ssl;
}

// bool openssl_accept(voidp peer){
//     auto code                       = SSL_accept((SSL *)peer);
//     return code 
// }

xinit(inc::the_main){
    inc::shared<inc::event_poll>    epoll(inc::default_init_by);
    inc::http::volatle_context      context;

    // 4,200,250,908
    // 123396
    // 30,000
    // xhint(inc::env::args[0], inc::env::args[1]);

    // inc::dns::get_address_v4(inc::env::args[1]).foreach([](inc::dns::ipv4_cstr ip){
    //     xhint(ip);
    // });
    // return;
    

    
    auto state = epoll->open();
    xhint("start service", state);

    voidp ssl_list[1024];
    voidp * ssl_ptr = ssl_list;

    inc::thread(xdetached{
        inc::tcp::service_v4 service;
        service.open_with_bind("127.0.0.1", 4433, inc::tcp::reuse_address_t::enable);
        service.listen(512);
        auto ctx = openssl_create_ctx(ssl_context_info{
            .cert_path = "./ca.crt",
            .private_key_path = "./ca.key",
            .role = ssl_role::server
        });

        while(true){
            auto client = service.accept();
            ssl_ptr[client.lend()] = openssl_create_ssl(ctx, client.lend());
            xhint(client.lend());
            
            // inc::tick t;
            // ixx i = 0;

            // for(i = 0; (code = SSL_accept((SSL *)ssl)) <= 0; i++){
            //     auto state = SSL_get_error((SSL *)ssl, code);
            //     // if (state == SSL_ERROR_WANT_READ){
            //     //     break;
            //     // }
            //     // xhint(i, code, state, SSL_ERROR_WANT_READ);
            // }

            // xhint(i, t, code);

            // inc::c08 content = 
            //     "HTTP/1.1 200\r\n"
            //     "Content-Type: text/html\r\n"
            //     "Set-Cookie: name0=VALUE0; path=/; expires=Sun, 16 Apr 2023 11:35:19 GMT; HttpOnly\r\n"
            //     "Content-Length: 46\r\n"
            //     "\r\n"
            //     "<html>"
            //         "<body>"
            //             "<h1>hello world</h1>"
            //         "</body>"
            //     "</html>";
            // auto r = SSL_write((SSL *)ssl, (asciis)content, content->length);
            // auto rx = SSL_get_error((SSL *)ssl, r);
            // xhint(r, rx);
            // xhint(code);

            
            epoll->append(client.hand_over(), inc::event_poll_flag_t::when_any);
        }
    });

    while(true){
        epoll->wait([&](inc::tcp::client_item_v4 & item, inc::event_poll_flag_t flag){
            auto ssl = (SSL *)ssl_list[item.lend()];
            auto code = SSL_accept(ssl);
            xhint(code, item.lend());

            if (code > 0){
                inc::c08 content = 
                    "HTTP/1.1 200\r\n"
                    "Content-Type: text/html\r\n"
                    "Set-Cookie: name0=VALUE0; path=/; HttpOnly\r\n"
                    "Content-Length: 46\r\n"
                    "\r\n"
                    "<html>"
                        "<body>"
                            "<h1>hello world</h1>"
                        "</body>"
                    "</html>";
                SSL_write((SSL *)ssl, (asciis)content, content->length);
            }
            return;

            auto pair = item->ip_port_pair();
            auto ip = pair->ip_cstr();
            auto port = pair->port->local_form();
            xhint(flag, ip, port);

            if (flag & inc::event_poll_flag_t::when_closed){
                xhint("closed");
                return;
            }

            inc::tick t;

            if (flag & inc::event_poll_flag_t::when_readable){
                char buffer[4096];
                auto len = item.read(buffer, sizeof(buffer));
                buffer[len] = '\0';
                // xhint("read", len, buffer);

                if (len < sizeof(buffer) - 1){
                    inc::unique_array<char> memory(buffer, ::length(len), not need_destruction, not need_free);
                    context.load(memory.hand_over());
                }

                uxx diff = t;
                xhint("read", diff);
            }

            if ((flag & inc::event_poll_flag_t::when_readable) and 
                (flag & inc::event_poll_flag_t::when_writeable)){
                auto name0 = inc::http::cookie{}
                    ->name("name0")
                    ->value("VALUE0")
                    ->max_age(60)
                ;

                // name0->expires = name0->expires;
                context.out.code = inc::http::code::ok;
                context.out.cookie.set(name0);
                // context.out.content = 
                //     "<html>"
                //         "<body>"
                //             "<h1>hello world</h1>"
                //         "</body>"
                //     "</html>"
                // ;

                // context.out.params_post.set("Name0", "miao");
                context.out.params_post.set("&", "小猫");

                context.output_to([&](inc::c08 const & content){
                    item.write_without_wait(content);
                });

                uxx diff = t;
                xhint("write", diff);
                // item.write_without_wait(
                //     "HTTP/1.1 200\r\n"
                //     "Content-Type: text/html\r\n"
                //     "Set-Cookie: name0=VALUE0; path=/; expires=Sun, 16 Apr 2023 11:35:19 GMT; HttpOnly\r\n"
                //     "Content-Length: 46\r\n"
                //     "\r\n"
                //     "<html>"
                //         "<body>"
                //             "<h1>hello world</h1>"
                //         "</body>"
                //     "</html>"
                // );
            }
        });
    }
};
